package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: fzykd
 * Date: 2022-10-31
 * Time: 13:05
 */

@Aspect //加了这个注解就表示一个切面
@Component //本来就是拦截的作用 所以在项目加载的时候 也要被加载
public class UserAspect {
    //定义切点 (设置拦截规则)
    @Pointcut("execution(* com.example.demo.controller.UserController.say(..))")
    public void pointcut(){}

    //实现通知方法

    /**
     * 定义pointcut 切点的前置通知
     * 在执行目标方法之前执行的方法 叫做前置通知
     * @Before("pointcut()") 括号不能忘记
     */
    @Before("pointcut()")
    public void doBefore(){
        System.out.println("执行前置通知！");
    }

    /**
     * 后置通知
     */
    @After("pointcut()")
    public void doAfter(){
        System.out.println("执行了后置通知");
    }

    /**
     * 返回之后的通知
     * 在return之后执行
     * 在后置通知之前
     */
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        System.out.println("执行了return通知");
    }

    /**
     * @AfterThrowing
     * 在 方法return之前出先了异常 就没有执行AfterReturning
     * 但抛出异常之后执行看AfterThrowing
     * 并且也执行了后置通知
     */
    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        System.out.println("执行了AfterThrowing");
    }

    /**
     * 最复杂的是环绕通知
     * 可以记录方法的开始时间和结束时间 要考虑多线程的情况下
     * 直接包裹 比前置还前  比后置还后
     */
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object result = null;

        System.out.println("环绕通知的前置方法!");
        try {
            //正确的是在这个里面去统计 这里面才是开始执行方法
            long state = System.currentTimeMillis();
            result = joinPoint.proceed(); //执行了目标方法 目标方法的后置通知也执行了
            long end = System.currentTimeMillis();
            long aaa = end-state;
            System.out.println("执行时间: " + aaa +"ms");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        System.out.println("环绕通知的后置方法");


        return result;
    }

}
